////////////////////////////////////////////////////////////////////////////
//
// File: bot.cpp
//
// Description:
//   This is the implementation file for the bot class.  
//
#include <iostream>
#include <stdlib.h>
#include <ctime>
#include <string>
#include <sstream>

using namespace std;

#include <qapplication.h>

#include "src/bot/bot.h"
#include "src/conGuy/types.h"
#include "src/conGuy/conGuy.h"
//#include "src/parser/parser.h"


// a hacked together chatclient for bnet.

bot::bot() {
  // Allocate memory for the conGuys.

  bnet = NULL;
  bnet = new conGuy(&chatDialog);
  //   uswest = new conGuy(&chatDialog);
  //   europe = new conGuy(&chatDialog);
  //   asia   = new conGuy(&chatDialog);

  checkForEventTimer = new QTimer(this);
  
  // Initialize the random generator for cointoss.
  srand( time(NULL) );

  connect(this, SIGNAL(appendChat(QString, QString)),
	  &chatDialog, SLOT(appendChat(QString, QString)));

  connect( bnet, SIGNAL( connectedToBnet() ),
           this, SLOT( startTimer() ) );

  connect(&chatDialog, SIGNAL(sendText(QString)),
	  this, SLOT(sendMsg(QString)));

  connect(checkForEventTimer, SIGNAL(timeout()),
	  this, SLOT(chat_event_handler()));

  chatDialog.show();
  exec();
}

bot::~bot() {

  bnet->disconnectNow();

  // For timestamp.
  time_t rawtime;
  struct tm * timeinfo;

  // All done!
  time ( &rawtime );
  timeinfo = localtime ( &rawtime );

  // Disconnect.
  if( bnet->isConnected() )
  {
      bnet->disconnectNow();
  }

  // Deallocate memory.
  delete bnet;
  bnet   = NULL;
}

bool bot::exec() {
  // For timestamp.
  time_t rawtime;
  struct tm * timeinfo;
  time ( &rawtime );
  timeinfo = localtime ( &rawtime );


  // Log timestamp of connection start.
  log("---------------------------------------------------------------");
  log(string("GiTMchat started at:                   ")
      + string(asctime(timeinfo)));
  log(string("---------")
      + string("------------------------------------------------------"));


  // Starts the connect-process.
  if( !(bnet->connectNow()) ) {
    done_ = true;
    return false;
  }

  return true;
}


// This function starts the timer that periodically checks for new
// Bnet packets.

bool bot::startTimer() {

    if(bnet->isConnected()) {
        unique_username = bnet->username();
        checkForEventTimer->start(100);
        return true;
    }
    else
    {
        return false;
    }


}  // end of bool bot::startTimer()



void bot::exit() {
  bnet->disconnectNow();
  close();
}



// This will emit the appendChat signal.
void bot::log(string msg) {
  emit appendChat(QString(msg.c_str()), QString("system"));
}

void bot::chatlog(string msg) {
  emit appendChat(QString(msg.c_str()), QString("chat"));
}

void bot::whisperlog(string msg) {
  emit appendChat(QString(msg.c_str()), QString("whisper"));  
}

void bot::serverlog(string msg) {
  emit appendChat(QString(msg.c_str()), QString("server"));
}
void bot::channellog(string msg) {
  emit appendChat(QString(msg.c_str()), QString("channel"));
}

void bot::melog(string msg) {
  emit appendChat(QString(msg.c_str()), QString("me"));  
}



void bot::sendMsg(QString msg) {
  string clientInput = string(msg.ascii());
  
  // If he typed /exit, then quit.
  if(clientInput == "/exit") {
      chatlog(get_timestamp() + string(" Client issued EXIT."));
      QApplication::exit( 0 );
  }
  else if(done_) {
    // If the user decided not to log in, ignore the msg.
    return;
  }
  else if(clientInput != "") {
    // Send this.
    sendQueue *toSend = bnet->getSendQueue();
    (toSend->mutex).lock();
    (toSend->out).push_back(clientInput);
    (toSend->mutex).unlock();
    melog(get_timestamp() + string(" <") + bnet->username() + string("> ")
	  + clientInput);
  }
}

// Return the current time in this form:   [4/18 17:38:04]
// for april 18th, at 5:38:04 pm.
string bot::get_timestamp() {
  string rv;
  char currTime[64];

  time_t rawtime;
  struct tm * timeinfo;

  time ( &rawtime );
  timeinfo = localtime ( &rawtime );

  sprintf(currTime, "[%d/%d %02d:%02d:%02d]",
	  timeinfo->tm_mon+1,
	  timeinfo->tm_mday,
	  timeinfo->tm_hour,
	  timeinfo->tm_min,
	  timeinfo->tm_sec);
	  
  rv = string(currTime);
  return rv;
}


// This is our big huge function that handles all chat events.

bool bot::chat_event_handler() {
    conGuy *myConGuy = bnet;

    new_event = myConGuy->gimmeEvent();

    // exit_now is set in conGuy if some error occurred and we should exit.
    // done is set through a ! triggered command by a user.
    if(new_event.exit_now == false && myConGuy->done == false) {
    
        if(new_event.try_again == false) {
            switch(new_event.eventID) {
            case EID_SHOWUSER:
                channellog(get_timestamp() + string(" User in channel: ")
                           + string(new_event.username));
                break;
            case EID_JOIN:
                channellog(get_timestamp() + string(" User joined channel: ")
                           + new_event.username);
                break;
            case EID_LEAVE:
                channellog(get_timestamp() + string(" User left channel: ")
                           + string(new_event.username));
                break;
            case EID_WHISPER:
                whisperlog(get_timestamp() + string(" Whisper from <") +
                           new_event.username + string("> ") + new_event.text);
                break;
            case EID_TALK:
                chatlog(get_timestamp() + string(" <") + new_event.username +
                        string("> ") + new_event.text);
                break;
            case EID_BROADCAST:
                serverlog(get_timestamp() + string(" Server broadcast: ") +
                          new_event.username + string(" ") + new_event.text);
                break;
            case EID_CHANNEL:
                channellog(get_timestamp() + 
                           string(" Channel: ") + new_event.text);
                break;
            case EID_USERFLAGS:
                // We only care if we have the ops or flag change.
                if(new_event.username == unique_username) 
                {
                    if(new_event.flags == USER_CHANNELOP) 
                    {
                        channellog(get_timestamp() + 
                                   string(" You have channel ops."));
                    }
                }
                break;
            case EID_WHISPERSENT:
                serverlog(get_timestamp() + 
                          string(" Whisper sent <") +
                          new_event.username + string("> ") + new_event.text);
                break;
            case EID_CHANNELFULL:
                serverlog(get_timestamp() + string(" Channel Full."));
                break;
            case EID_CHANNELDOESNOTEXIST:
                serverlog(get_timestamp() + 
                          string(" Channel does not exist."));
                break;
            case EID_CHANNELRESTRICTED:
                serverlog(get_timestamp() + string(" Channel is restricted."));
                break;
            case EID_INFO:
                serverlog(get_timestamp() + 
                          string(" Info: ") + new_event.username +
                          string(" ") + new_event.text);
                break;
            case EID_ERROR: 
                serverlog(get_timestamp() + 
                          string(" Error: ") + new_event.username +
                          string(" ") + new_event.text);
                break;
            case EID_EMOTE:
                serverlog(get_timestamp() + 
                          string(" Emote: ") + new_event.username +
                          string("    ") + new_event.text);
                break;
            default:
                serverlog(get_timestamp() + string(" Unknown event ID."));
                break;
            }
        }
    }

    if(new_event.exit_now == true) {
        // Return false because some kind of failure happened in conGuy.
        // Kill the timer.
        checkForEventTimer->stop();
        return false;
    }
    else if(myConGuy->done == true) {
        // Return true because we finished by means of some ! triggered command
        //   in chat.
        checkForEventTimer->stop();
        this->exit();
        return true;
    }

    // If it gets here, then it did everything correctly.  (timer went
    // off and it got an event.
    return true;
}


